package org.example.mq.mqserver.core;

import jakarta.annotation.PostConstruct;
import lombok.Data;
import org.apache.catalina.Host;
import org.apache.ibatis.annotations.Param;
import org.example.mq.common.exception.MqException;
import org.example.mq.mqserver.datacenter.DiskDataCenter;
import org.example.mq.mqserver.datacenter.MemoryDataCenter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.validation.ObjectError;

import java.io.IOException;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

/*
    虚拟主机类
    每个虚拟主机管理自己的交换机，队列，绑定，消息，数据
 */
@Component
@Data
@Scope("prototype") 
public class VirtualHost {
    public String HostName;

    @Autowired
    private DiskDataCenter diskDataCenter;

    private final MemoryDataCenter memoryDataCenter = new MemoryDataCenter();
    private Route route = new Route();

    private Object exchangeLocker = new Object();
    private Object queueLocker = new Object();


    private ConsumerManager consumerManager = new ConsumerManager(this);

//    private final AtomicInteger consumerTagCounter = new AtomicInteger(1);

    @PostConstruct
    public void init() {
        diskDataCenter.init();
        try {
            memoryDataCenter.recovery(diskDataCenter);
        } catch (IOException | InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
//@PostConstruct
//public void init() {
//    System.out.println("diskDataCenter = " + diskDataCenter);
//    if (diskDataCenter == null) {
//        throw new IllegalStateException("diskDataCenter 注入失败");
//    }
//    diskDataCenter.init();
//    try {
//        memoryDataCenter.recovery(diskDataCenter);
//    } catch (IOException | InterruptedException e) {
//        throw new RuntimeException(e);
//    }
//}

    //HostName_ExchangeName HostName_QueueName 前面的hostname来区分host

    //创建交换机
    public boolean exchangeDeclare(String exchangeName, ExchangeType exchangeType, boolean durable,
                                   boolean autoDelete, Map<String,Object>arguments){
        exchangeName = HostName + "_" + exchangeName;

        try {
            synchronized (exchangeLocker){
                //查询交换机是否已存在
                Exchange existExchange = memoryDataCenter.getExchange(exchangeName);
                if(existExchange!=null){
                    return true;
                }

                //插入交换机
                Exchange exchange = new Exchange();
                exchange.setName(exchangeName);
                exchange.setType(exchangeType);
                exchange.setDurable(durable);
                exchange.setAutoDelete(autoDelete);
                exchange.setArguments(arguments);

                if(durable){
                    //持久化
                    diskDataCenter.insertExchange(exchange);
                }

                memoryDataCenter.insertExchange(exchange);
            }
            System.out.println("[VirtualHost]创建交换机：" + exchangeName + "完成!");
            return true;
        } catch (Exception e) {
            System.out.println("[VirtualHost]创建交换机：" + exchangeName + "失败!");
            e.printStackTrace();
            return false;
        }
    }

    //删除交换机
    public boolean deleteExchange(String exchangeName){
        exchangeName = HostName+ "_" + exchangeName;

        try{
            synchronized (exchangeLocker){
                //查询交换机
                Exchange exchange = memoryDataCenter.getExchange(exchangeName);

                if(exchange==null){
                    System.out.println("[VirtualHost]删除，交换机:"+exchangeName+"不存在！");
                    return true;
                }

                boolean ok = diskDataCenter.deleteExchange(exchangeName);
                if(!ok){
                    System.out.println("[VirtualHost]物理删除Exchange:" + exchangeName + "失败！");
                    return false;
                }

                ok = memoryDataCenter.delExchange(exchangeName);
                if(!ok){
                    System.out.println("[VirtualHost]内存删除Exchange:" + exchangeName + "失败！");
                    return false;
                }
            }
            System.out.println("[VirtualHost]删除交换机:" + exchangeName + "成功！");
            return true;
        }catch (Exception e){
            System.out.println("[VirtualHost]删除交换机:" + exchangeName + "失败！");
            e.printStackTrace();
            return false;
        }
    }


    //创建队列
    public boolean queueDeclare(String queueName, boolean durable,
                                boolean exclusive, boolean autoDelete, Map<String, Object>arguments){
        queueName = HostName + "_" + queueName;

        try {
            synchronized (queueLocker){
                //检查是否存在
                MessageQueue existQueue = memoryDataCenter.getQueue(queueName);
                if (existQueue != null) {
//                    System.out.println("[VirtualHost]创捷队列:" + queueName + ",已存在！");
                    throw new MqException("[VirtualHost]创捷队列:" + queueName + ",已存在！");
                }

                MessageQueue messageQueue = new MessageQueue();

                messageQueue.setName(queueName);
                messageQueue.setDurable(durable);
                messageQueue.setAutoDelete(autoDelete);
                messageQueue.setExclusive(exclusive);
                messageQueue.setArguments(arguments);

                if (durable) {
                    diskDataCenter.insertMessageQueue(messageQueue);
                }

                memoryDataCenter.insertQueue(messageQueue);
            }
            System.out.println("[VirtualHost]创建队列：" + queueName + "；成功！");
            return true;
        }catch (Exception e){
            System.out.println("[VirtualHost]创建队列：" + queueName + "；失败！");
            e.printStackTrace();
            return false;
        }
    }


    //删除队列
    public boolean delQueue(String queueName){
        queueName = HostName+ "_" +queueName;

        try {
            synchronized (queueLocker){
                MessageQueue queue = memoryDataCenter.getQueue(queueName);
                if(queue==null){
//                    System.out.println("[VirtualHost]删除队列："+queueName+"不存在!");
                    throw new MqException("[VirtualHost]删除队列："+queueName+"不存在!");
                }

                boolean ok = diskDataCenter.deleteMessageQueue(queueName);
                if(!ok){
                    throw new MqException("[VirtualHost]diskDataCenter删除queue："+queueName+"；失败");
                }

                ok = memoryDataCenter.delQueue(queueName);
                if(!ok){
                    throw new MqException("[VirtualHost]memoryDataCenter删除queue:"+queueName+"；失败");
                }
            }
            System.out.println("[VirtualHost]diskDataCenter删除queue："+queueName+"；成功!");
            return true;
        }catch (Exception e){
            System.out.println("[VirtualHost]删除队列："+queueName+";失败!");
            e.printStackTrace();
            return false;
        }
    }

    //交换机队列绑定
    public boolean queueBind(String exchangeName,String queueName,String bindingKey){
        exchangeName = HostName + "_" + exchangeName;
        queueName = HostName + "_" + queueName;

        try {
            synchronized (exchangeLocker){
                synchronized (queueLocker){
                    Exchange exchange = memoryDataCenter.getExchange(exchangeName);
                    MessageQueue queue = memoryDataCenter.getQueue(queueName);

                    if(exchange==null){
                        throw new MqException("[VirtualHost:queueBind]指定交换机:"+exchangeName+"不存在！");
                    }

                    if(queue==null){
                        throw new MqException("[VirtualHost:queueBind]指定队列:"+queueName+"不存在!");
                    }

                    //1.判断BindingKey是否合法
                    if (!route.checkBindingKey(bindingKey,exchange.getType())) {
                        throw new MqException("[Route]checkBindingKey:"+bindingKey+"合法性校验失败！");
                    }

                    Bind bind = new Bind();
                    bind.setExchangeName(exchangeName);
                    bind.setQueueName(queueName);
                    bind.setBindingKey(bindingKey);

                    //这里如果routingKey重复或者其他异常则中断
                    memoryDataCenter.insertBinding(bind);


                    //3.检查持久化 如果队列或者交换机没有持久化 则无需进行持久化
                    if(exchange.isDurable() && queue.isDurable()){
                        diskDataCenter.insertBind(bind);
                    }
                }
            }
            System.out.println("[VirtualHost:queueBind]exchange:"+exchangeName+",queue:"+queueName+"绑定成功！");
            return true;
        }catch (Exception e){
            System.out.println("[VirtualHost]queue:"+queueName+",exchange:"+exchangeName+"绑定失败！");
            e.printStackTrace();
            return false;
        }
    }

    //队列取消所有绑定
    public boolean queueUnAllBind(String queueName){
        queueName = HostName + "_" +queueName;

        //TODO

        return true;
    }

    //交换机取消绑定
    public boolean exchangeUnBind(String exchangeName){
        exchangeName = HostName + "_" + exchangeName;

        try{
            synchronized (exchangeLocker){
                Exchange exchange = memoryDataCenter.getExchange(exchangeName);

                if(exchange==null){
                    throw new MqException("[VirtualHost]exchange:"+exchangeName+"；取消绑定失败，该交换机不存在!");
                }

                ConcurrentHashMap<String, Set<String>> binds = memoryDataCenter.delExchangeBind(exchangeName);

                if(binds!=null){
                    for (Map.Entry<String, Set<String>> entry : binds.entrySet()) {
                        String queueName = entry.getKey();

                        Set<String> routingKeys = entry.getValue();

                        for (String routingKey : routingKeys) {
                            diskDataCenter.deleteBind(exchangeName,queueName,routingKey);
                        }
                    }
                }
            }

            return true;
        }catch (Exception e){
            System.out.println("[VirtualHost]exchange："+exchangeName+"；取消绑定失败");
            e.printStackTrace();
            return false;
        }
    }

    //队列&&交换机取消指定绑定
    public boolean queueUnBind(String queueName,String exchangeName){
        queueName = HostName + "_" +queueName;
        exchangeName = HostName + "_" + exchangeName;

        try{
            synchronized (exchangeLocker){
                synchronized (queueLocker){
                    //判断目标是否存在
                    MessageQueue queue = memoryDataCenter.getQueue(queueName);
                    Exchange exchange = memoryDataCenter.getExchange(exchangeName);

                    if(queue==null || exchange==null){
                        throw new MqException("[VirtualHost]交换机或者队列不存在无法取消绑定");
                    }

                    ConcurrentHashMap<String, Set<String>> bindings = memoryDataCenter.getBindings(exchangeName);
                    if(bindings == null)return true;

                    if(!bindings.containsKey(queueName))return true;

                    Set<String> remove = bindings.remove(queueName);
                    for (String bindingKey : remove) {
                        diskDataCenter.deleteBind(exchangeName,queueName,bindingKey);
                    }
                }
            }
            System.out.println("[VirtualHost]exchange:"+exchangeName+"queue:"+queueName+"取消绑定成功！");
            return true;
        }catch (Exception e){
            System.out.println("[VirtualHost]exchange:"+exchangeName+"queue:"+queueName+"取消绑定失败！");
            e.printStackTrace();
            return false;
        }
    }

    //发送消息到指定交换机
    public boolean basicPublish(String exchangeName,String routingKey,BasicProperties basicProperties,byte[] body){
        exchangeName = HostName + "_" + exchangeName;

        try{
            //检测交换机是否存在
            Exchange exchange = memoryDataCenter.getExchange(exchangeName);
            if(exchange==null){
                throw new MqException("[VirtualHost:basicPublish]发送消息失败,exchange:"+exchangeName+";不存在");
            }

            if(!route.checkRoutingKey(routingKey)){
                throw new MqException("[VirtualHost]：routingKey："+routingKey+"非法！");
            }

            //构建消息
            Message message = Message.createMessageWithID(basicProperties,routingKey,body);


            if(exchange.getType()==ExchangeType.DIRECT){
                //先找到改交换机所绑定的队列去查询是否等于bindingKey
                ConcurrentHashMap<String, Set<String>> bindings = memoryDataCenter.getBindings(exchangeName);

                if(bindings!=null) {
                    for (Map.Entry<String, Set<String>> entry : bindings.entrySet()) {
                        String queueName = entry.getKey();

                        /*
                            如果这里是默认交换机则可以判断
                            if queueName = bindingKey
                            则直接进行转发
                         */
                        //TODO

                        Set<String> bindingKeys = entry.getValue();

                        //如果等于bandingKey则转发
                        for (String bindingKey : bindingKeys) {
                            if(bindingKey.equals(routingKey)){
                                sendMessage(queueName,message);
                                break;
                            }
                        }
                    }
                }
                //这个应该放在默认交换机的规则中
//                else{
//                    //根据queueName转发
//                    String queueName = HostName + "_" + routingKey;
//
//                    MessageQueue queue = memoryDataCenter.getQueue(queueName);
//
//                    if(queue!=null){
//                        sendMessage(queueName,message);
//                    }
//                    else {
//                        throw new MqException("[VirtualHost:basicPublish]发送消息失败：交换机暂未绑定队列");
//                    }
//                }
            }
            else if(exchange.getType()==ExchangeType.FANOUT){
                //fanout 转发
                ConcurrentHashMap<String, Set<String>> bindings = memoryDataCenter.getBindings(exchangeName);
                //对于FANOUT来说直接转发给绑定的队列即可
                for(Map.Entry<String,Set<String>> entry : bindings.entrySet()){
                    String queueName = entry.getKey();
//                    queueName = HostName+"_"+queueName;
                    //这里不做队列存在判断因为在其他控制中心发送函数中已经进行封装

                    sendMessage(queueName,message);
                }
            }
            else{
                //topic 模式
                ConcurrentHashMap<String, Set<String>> bindings = memoryDataCenter.getBindings(exchangeName);

                for(Map.Entry<String,Set<String>> entry : bindings.entrySet()){
                    String queueName = entry.getKey();
                    Set<String> bindingKeys = entry.getValue();

                    //这里不做队列存在判断因为在其他控制中心发送函数中已经进行封装

                    for (String bindingKey : bindingKeys) {
                        if(!route.topicRoute(routingKey,bindingKey)){
                            continue;
                        }
                        sendMessage(queueName,message);
                        break;
                    }
                }
            }
            System.out.println("[VirtualHost:]发送消息成功！");
            return  true;
        }catch (Exception e){
            System.out.println("[VirtualHost:]发送消息失败！");
            e.printStackTrace();
            return false;
        }

    }

    private void sendMessage(String queueName, Message message) throws InterruptedException, IOException {
        //写入硬盘和内存
        int deliverMode = message.getBasicProperties().getDeliverMode();

        MessageQueue queue = memoryDataCenter.getQueue(queueName);
        //2持久化 1不持久化
        if(deliverMode==2){
            //写入硬盘
            diskDataCenter.writeMessageToDisk(queue,message);
        }

        //写入内存
        memoryDataCenter.sendMessage(queue,message);

        // 通知调度中心该队列有消息（非重复检测版本）
        consumerManager.notifyMessage(queueName);
    }

    public boolean basicConsume(String consumerTag,String channelId,String queueName,boolean autoAck,Consumer callback){
        //注册消费者
        queueName = HostName+ "_" + queueName;
        try{
            consumerManager.register(consumerTag,channelId,queueName,autoAck,callback);

            consumerManager.notifyMessage(queueName);
            return true;
        }catch (Exception e){
            System.out.println("[ConsumerManager]注册消费者失败!");
            e.printStackTrace();
            return false;
        }
    }

    public boolean basicConsume(String channelId,String queueName, boolean autoAck, Consumer callback) {
        // 自动生成短且可读的 consumerTag
        String consumerTag = "C-" + UUID.randomUUID().toString();
        return basicConsume(consumerTag,channelId,queueName, autoAck, callback);
    }


    public boolean basicAck(String queueName,String messageId){
        queueName  = HostName + "_" + queueName;

        try {
            //判断队列是否存在
            MessageQueue queue = memoryDataCenter.getQueue(queueName);
            if(queue==null){
                throw new MqException("[VirtualHost:basicAck()]队列:"+queueName+"不存在");
            }

            Message message = memoryDataCenter.getMessage(messageId);
            if(message==null){
                throw new MqException("[VirtualHost:basicAck()]messageId错误");
            }

            //删除逻辑
            memoryDataCenter.delAckMessage(queueName,messageId);

            if(message.isDelivery()){
                diskDataCenter.deleteMessage(queue,message);
            }

            memoryDataCenter.delMessage(messageId);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("[VirtualHost:basicAck()]消息确认失败");
            return false;
        }
    }
}
